use crate::{
    impl_interfacetype::private_associated_type,
    my_visibility::{RelativeVis, VisibilityKind},
    parse_utils::parse_str_as_ident,
    workaround::token_stream_to_string,
    *,
};

use std::marker::PhantomData;

use proc_macro2::TokenStream as TokenStream2;

use quote::TokenStreamExt;

use syn::ItemTrait;

use as_derive_utils::{
    gen_params_in::{GenParamsIn, InWhat},
    to_token_fn::ToTokenFnMut,
};

mod attribute_parsing;
mod common_tokens;
mod impl_delegations;
mod lifetime_unelider;
mod method_where_clause;
mod methods_tokenizer;
mod replace_self_path;
mod trait_definition;

#[cfg(test)]
mod tests;

use self::{
    attribute_parsing::SabiTraitOptions,
    common_tokens::{CommonTokens, IsStaticTrait, LifetimeTokens},
    lifetime_unelider::LifetimeUnelider,
    method_where_clause::MethodWhereClause,
    methods_tokenizer::MethodsTokenizer,
    trait_definition::{TraitDefinition, TraitMethod},
};

/// Variables passed to all the `*_items` functions here.
#[allow(dead_code)]
#[derive(Copy, Clone)]
struct TokenizerParams<'a> {
    arenas: &'a Arenas,
    ctokens: &'a CommonTokens,
    config: &'a SabiTraitOptions<'a>,
    trait_def: &'a TraitDefinition<'a>,
    vis: VisibilityKind<'a>,
    submod_vis: RelativeVis<'a>,
    totrait_def: &'a TraitDefinition<'a>,
    vtable_trait_decl: &'a TraitDefinition<'a>,
    vtable_trait_impl: &'a TraitDefinition<'a>,
    trait_ident: &'a syn::Ident,
    trait_to: &'a syn::Ident,
    trait_backend: &'a syn::Ident,
    trait_interface: &'a syn::Ident,
    make_vtable_ident: &'a syn::Ident,
    trait_cto_ident: &'a syn::Ident,
    /// TokenStreams that don't have a `'lt,` if the trait object requires
    /// `'static` to be constructed.
    lt_tokens: &'a LifetimeTokens,
}

/// The implementation of the `#[sabi_trait]` proc-macro attribute.
pub fn derive_sabi_trait(item: ItemTrait) -> Result<TokenStream2, syn::Error> {
    let arenas = Arenas::default();
    let arenas = &arenas;
    let ctokens = CommonTokens::new();
    let ctokens = &ctokens;

    let trait_ident = &item.ident;

    let config = &self::attribute_parsing::parse_attrs_for_sabi_trait(&item, arenas, ctokens)?;

    let trait_def = &config.trait_definition;
    let lt_tokens = &LifetimeTokens::new(trait_def.is_static);
    let vis = trait_def.vis;
    let submod_vis = trait_def.submod_vis;

    let totrait_def = &trait_def.replace_self(WhichItem::TraitObjectImpl)?;
    let vtable_trait_decl = &trait_def.replace_self(WhichItem::VtableDecl)?;
    let vtable_trait_impl = &trait_def.replace_self(WhichItem::VtableImpl)?;

    let generated_mod = &parse_str_as_ident(&format!("{}_trait", trait_ident));
    let trait_to = &parse_str_as_ident(&format!("{}_TO", trait_ident));
    let trait_backend = &parse_str_as_ident(&format!("{}_Backend", trait_ident));
    let trait_interface = &parse_str_as_ident(&format!("{}_Interface", trait_ident));
    let make_vtable_ident = &parse_str_as_ident(&format!("{}_MV", trait_ident));
    let trait_cto_ident = &parse_str_as_ident(&format!("{}_CTO", trait_ident));

    let mut mod_contents = TokenStream2::default();

    let tokenizer_params = TokenizerParams {
        arenas,
        ctokens,
        config,
        lt_tokens,
        trait_def,
        vis,
        submod_vis,
        totrait_def,
        vtable_trait_decl,
        vtable_trait_impl,
        trait_ident,
        trait_to,
        trait_backend,
        trait_interface,
        make_vtable_ident,
        trait_cto_ident,
    };

    first_items(tokenizer_params, &mut mod_contents);

    constructor_items(tokenizer_params, &mut mod_contents);

    trait_and_impl(tokenizer_params, &mut mod_contents);

    methods_impls(tokenizer_params, &mut mod_contents)?;

    declare_vtable(tokenizer_params, &mut mod_contents);

    vtable_impl(tokenizer_params, &mut mod_contents);

    impl_delegations::delegated_impls(tokenizer_params, &mut mod_contents);

    let doc_hidden_attr = config.doc_hidden_attr;

    let mod_docs = if doc_hidden_attr.is_none() {
        Some(format!(
            "This module is generated by the \
             [`#[sabi_trait]`](macro@::abi_stable::sabi_trait) \
             attribute on \
            [{trait_}](trait@{trait_})",
            trait_ = trait_ident,
        ))
    } else {
        None
    }
    .into_iter();

    let mut tokens = quote!(
        #doc_hidden_attr
        #[doc(inline)]
        #vis use self::#generated_mod::{
            #trait_to,
            #trait_ident,
            #trait_cto_ident,
        };

        #doc_hidden_attr
        #(#[doc = #mod_docs])*
        #[allow(explicit_outlives_requirements)]
        #vis mod #generated_mod{
            #mod_contents
        }
    );

    if config.debug_output_tokens {
        let tokens_str = tokens.to_string();
        tokens.append_all(quote!(
            pub const TOKENS: &'static str = #tokens_str;
        ));
    }

    // drop(_measure_time1);
    if config.debug_print_trait {
        panic!("\n\n\n{}\n\n\n", token_stream_to_string(tokens.clone()));
    }

    Ok(tokens)
}

/// Outputs these items:
///
/// - `Trait_Backend`:
///     A type alias to the underlying implementation of the trait object,
///     which is either RObject
///
/// - `Trait_Interface`
///     A marker type describing the traits that are required when constructing
///     the underlying implementation of the trait object,
///     and are then implemented by it,
///     by implementing `InterfaceType`.
///
/// - `Trait_TO`:
///     The ffi-safe trait object for the trait.
///
fn first_items(
    TokenizerParams {
        config,
        ctokens,
        lt_tokens,
        trait_def,
        submod_vis,
        trait_to,
        trait_backend,
        trait_interface,
        trait_cto_ident,
        ..
    }: TokenizerParams,
    mod_: &mut TokenStream2,
) {
    let trait_ident = trait_def.name;

    let doc_hidden_attr = config.doc_hidden_attr;

    let mut uto_params = trait_def.generics_tokenizer(
        InWhat::ItemDecl,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_erasedptr,
    );
    uto_params.set_no_bounds();

    let mut gen_params_header_rref = trait_def.generics_tokenizer(
        InWhat::ImplHeader,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_sub_lt,
    );
    gen_params_header_rref.set_no_bounds();

    let gen_params_use_to_rref = trait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_rref,
    );

    let uto_params_use = trait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_erasedptr,
    );

    let mut trait_interface_header = trait_def.generics_tokenizer(
        InWhat::ImplHeader,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_empty,
    );
    trait_interface_header.set_no_bounds();

    let mut trait_interface_decl = trait_def.generics_tokenizer(
        InWhat::ItemDecl,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_empty,
    );
    trait_interface_decl.set_no_bounds();
    // trait_interface_decl.set_unsized_types();

    let trait_interface_use = trait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_empty,
    );

    let to_params = trait_def.generics_tokenizer(
        InWhat::ItemDecl,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_erasedptr,
    );

    let where_preds = (&trait_def.where_preds).into_iter();

    let vtable_args = trait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_unit_erasedptr,
    );

    let impld_traits = trait_def.impld_traits.iter().map(|x| &x.ident);
    let impld_traits_a = impld_traits.clone();
    let impld_traits_b = impld_traits.clone();

    let unimpld_traits_a = trait_def.unimpld_traits.iter().cloned();
    let unimpld_traits_b = trait_def.unimpld_traits.iter().cloned();

    let priv_assocty = private_associated_type();

    let object = match trait_def.which_object {
        WhichObject::DynTrait => quote!(DynTrait),
        WhichObject::RObject => quote!(RObject),
    };
    let vtable_argument = match trait_def.which_object {
        WhichObject::DynTrait => quote!(__sabi_re::PrefixRef<VTable_Prefix<#vtable_args>>),
        WhichObject::RObject => quote!(VTable_Prefix<#vtable_args>),
    };

    let dummy_struct_generics = trait_def.generics_tokenizer(
        InWhat::DummyStruct,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_empty,
    );
    // dummy_struct_generics.set_unsized_types();

    let used_trait_object = quote!(#trait_backend<#uto_params_use>);

    let trait_flags = &trait_def.trait_flags;
    let send_syncness = match (trait_flags.sync, trait_flags.send) {
        (false, false) => "UnsyncUnsend",
        (false, true) => "UnsyncSend",
        (true, false) => "SyncUnsend",
        (true, true) => "SyncSend",
    }
    .piped(parse_str_as_ident);

    let mut trait_backend_docs = String::new();

    let mut trait_interface_docs = String::new();

    let mut trait_to_docs = String::new();

    let mut trait_cto_docs = String::new();
    if doc_hidden_attr.is_none() {
        trait_backend_docs = format!(
            "An alias for the underlying implementation of \
             [`{trait_to}`](struct@{trait_to}).\
            ",
            trait_to = trait_to
        );
        trait_interface_docs = format!(
            "A marker type describing the traits that are required when constructing \
             [`{trait_to}`](struct@{trait_to}),\
             and are then implemented by it,
             by implementing the 
             [`InterfaceType`](::abi_stable::InterfaceType)
             trait.",
            trait_to = trait_to
        );
        trait_to_docs = format!(
            "\
            The trait object for [{Trait}](trait@{Trait}).\n\
            \n\
            There are extra methods on the `obj` field.\n
            ",
            Trait = trait_ident
        );
        trait_cto_docs = format!(
            "A type alias for the const-constructible \
             [`{trait_to}`](struct@{trait_to}).",
            trait_to = trait_to
        );
    }
    let one_lt = &lt_tokens.one_lt;

    quote!(
        use super::*;

        use abi_stable::sabi_trait::reexports::{*,__sabi_re};

        use self::#trait_ident as __Trait;

        #[doc=#trait_backend_docs]
        #submod_vis type #trait_backend<#uto_params>=
            __sabi_re::#object<
                #one_lt
                _ErasedPtr,
                #trait_interface<#trait_interface_use>,
                #vtable_argument
            >;

        #[doc=#trait_cto_docs]
        #submod_vis type #trait_cto_ident<#gen_params_header_rref>=
            #trait_to<#gen_params_use_to_rref>;



        #[doc=#trait_interface_docs]
        #[repr(C)]
        #[derive(::abi_stable::StableAbi)]
        #submod_vis struct #trait_interface<#trait_interface_decl>(
            __sabi_re::NonOwningPhantom<(#dummy_struct_generics)>
        );

        impl<#trait_interface_header> #trait_interface<#trait_interface_use> {
            /// Constructs this type
            #submod_vis const NEW:Self=#trait_interface(__sabi_re::NonOwningPhantom::NEW);
        }


        #[doc=#trait_to_docs]
        #[repr(transparent)]
        #[derive(::abi_stable::StableAbi)]
        #[sabi(bound(#used_trait_object: ::abi_stable::StableAbi))]
        #submod_vis struct #trait_to<#to_params>
        where
            _ErasedPtr:__GetPointerKind,
            #(#where_preds)*
        {
            ///
            #submod_vis obj:#used_trait_object,
            _marker:__sabi_re::UnsafeIgnoredType< __sabi_re::#send_syncness >,
        }

        const __inside_generated_mod:()={
            use abi_stable::{
                InterfaceType,
                type_level::{
                    impl_enum::{Implemented,Unimplemented},
                    trait_marker,
                },
            };

            impl<#trait_interface_header>
                abi_stable::InterfaceType
                for #trait_interface<#trait_interface_use>
            {
                #( type #impld_traits_a=Implemented<trait_marker::#impld_traits_b>; )*
                #( type #unimpld_traits_a=Unimplemented<trait_marker::#unimpld_traits_b>; )*
                type #priv_assocty=();
            }
        };

    )
    .to_tokens(mod_);
}

/// Outputs the trait object constructors.
fn constructor_items(params: TokenizerParams<'_>, mod_: &mut TokenStream2) {
    let TokenizerParams {
        ctokens,
        totrait_def,
        submod_vis,
        trait_ident,
        trait_to,
        trait_backend,
        trait_interface,
        lt_tokens,
        make_vtable_ident,
        ..
    } = params;

    let doc_hidden_attr = params.config.doc_hidden_attr;

    let trait_params =
        totrait_def.generics_tokenizer(InWhat::ItemUse, WithAssocTys::No, &ctokens.empty_ts);

    let assoc_tys_a = totrait_def.assoc_tys.keys();
    let assoc_tys_b = assoc_tys_a.clone();
    let assoc_tys_c = assoc_tys_a.clone();
    let assoc_tys_d = assoc_tys_a.clone();
    let assoc_tys_e = assoc_tys_a.clone();
    let assoc_tys_f = assoc_tys_a.clone();

    let mut make_vtable_args = totrait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::No,
        &ctokens.ts_make_vtable_args,
    );
    make_vtable_args.skip_lifetimes();

    let mut make_vtable_args_const = totrait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::No,
        &ctokens.ts_make_vtable_args_const,
    );
    make_vtable_args_const.skip_lifetimes();

    let fn_can_it_downcast_arg = match totrait_def.which_object {
        WhichObject::DynTrait => quote!(Downcasting),
        WhichObject::RObject => quote!(),
    };

    let trait_interface_use = totrait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_empty,
    );

    let one_lt = &lt_tokens.one_lt;

    let extra_constraints_ptr = match totrait_def.which_object {
        WhichObject::DynTrait => quote!(
            #trait_interface<#trait_interface_use>:
                ::abi_stable::erased_types::InterfaceType,
            __sabi_re::DynTraitVTable_Ref<
                #one_lt
                _OrigPtr::TransmutedPtr,
                #trait_interface<#trait_interface_use>,
            >:
                __sabi_re::MakeDynTraitVTable<
                    #one_lt
                    _OrigPtr::PtrTarget,
                    _OrigPtr,
                    Downcasting
                >,
        ),
        WhichObject::RObject => quote!(),
    };

    let extra_constraints_value = match totrait_def.which_object {
        WhichObject::DynTrait => quote!(
            #trait_interface<#trait_interface_use>:
                ::abi_stable::erased_types::InterfaceType,
            __sabi_re::DynTraitVTable_Ref<
                #one_lt
                __sabi_re::RBox<()>,
                #trait_interface<#trait_interface_use>,
            >:
                __sabi_re::MakeDynTraitVTable<
                    #one_lt
                    _Self,
                    __sabi_re::RBox<_Self>,
                    Downcasting
                >,
        ),
        WhichObject::RObject => quote!(),
    };

    let extra_constraints_const = match totrait_def.which_object {
        WhichObject::DynTrait => quote!(
            #trait_interface<#trait_interface_use>:
                ::abi_stable::erased_types::InterfaceType,
            __sabi_re::DynTraitVTable_Ref<
                #one_lt
                __sabi_re::RRef<'_sub, ()>,
                #trait_interface<#trait_interface_use>,
            >:
                __sabi_re::MakeDynTraitVTable<
                    #one_lt
                    _Self,
                    &'_sub _Self,
                    Downcasting
                >,
        ),
        WhichObject::RObject => quote!(),
    };

    let gen_params_header = totrait_def.generics_tokenizer(
        InWhat::ImplHeader,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_erasedptr,
    );

    let gen_params_use_to = totrait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_erasedptr,
    );

    let gen_params_header_rbox = totrait_def.generics_tokenizer(
        InWhat::ImplHeader,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt,
    );

    let gen_params_use_to_rbox = totrait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_rbox,
    );

    let uto_params_use = totrait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_erasedptr,
    );

    let trait_interface_use = totrait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_empty,
    );

    let gen_params_header_rref = totrait_def.generics_tokenizer(
        InWhat::ImplHeader,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_sub_lt,
    );

    let gen_params_use_to_rref = totrait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_rref,
    );

    let mut shared_docs = String::new();
    let mut from_ptr_docs = String::new();
    let mut from_value_docs = String::new();
    let mut from_const_docs = String::new();

    if doc_hidden_attr.is_none() {
        shared_docs = "\
            <br><br>\
            `can_it_downcast` describes whether the trait object can be \
            converted back into the original type or not.<br>\n\
            Its possible values are `TD_CanDowncast` and `TD_Opaque`.\n\
        "
        .to_string();

        from_ptr_docs = format!(
            "Constructs this trait object from a pointer to a type that implements `{trait_}`.\n\
             \n\
             This method is automatically generated,\n\
             for more documentation you can look at\n\
             [`abi_stable::docs::sabi_trait_inherent#from_ptr-method`]\n\
            ",
            trait_ = trait_ident
        );

        from_value_docs = format!(
            "Constructs this trait from a type that implements `{trait_}`.\n\
             \n\
             This method is automatically generated,\n\
             for more documentation you can look at\n\
             [`abi_stable::docs::sabi_trait_inherent#from_value-method`]\n\
            ",
            trait_ = trait_ident
        );

        from_const_docs = format!(
            "Constructs this trait from a constant of a type that implements `{trait_}`.\n\
             \n\
             This method is automatically generated,\n\
             for more documentation you can look at\n\
             [`abi_stable::docs::sabi_trait_inherent#from_const-method`]\n\
              \n\
             You can construct the `vtable_for` parameter with \
             [`{make_vtable_ident}::VTABLE`].\n\
            ",
            trait_ = trait_ident,
            make_vtable_ident = make_vtable_ident,
        );
    }

    let reborrow_methods = reborrow_methods_tokenizer(params);

    let plus_lt = &lt_tokens.plus_lt;

    let constructing_backend = match totrait_def.which_object {
        WhichObject::DynTrait => quote!(
            #trait_backend::from_const(
                ptr,
                can_it_downcast,
                #make_vtable_ident::<#make_vtable_args_const>::VTABLE_INNER
            )
        ),
        WhichObject::RObject => quote!({
            let _ = __sabi_re::ManuallyDrop::new(can_it_downcast);
            #trait_backend::with_vtable_const::<_, Downcasting>(
                ptr,
                #make_vtable_ident::<#make_vtable_args_const>::VTABLE_INNER
            )
        }),
    };

    quote!(
        impl<#gen_params_header> #trait_to<#gen_params_use_to>
        where
            _ErasedPtr: __sabi_re::AsPtr<PtrTarget = ()>,
        {
            #[doc=#from_ptr_docs]
            #[doc=#shared_docs]
            #submod_vis fn from_ptr<_OrigPtr,Downcasting>(
                ptr:_OrigPtr,
                can_it_downcast:Downcasting,
            )->Self
            where
                _OrigPtr:
                    __sabi_re::CanTransmuteElement<(),TransmutedPtr=_ErasedPtr>,
                _OrigPtr::PtrTarget:
                    #trait_ident<#trait_params #( #assoc_tys_a= #assoc_tys_b, )* >+
                    Sized
                    #plus_lt,
                #trait_interface<#trait_interface_use>:
                    __sabi_re::GetRObjectVTable<
                        Downcasting,_OrigPtr::PtrTarget,_ErasedPtr,_OrigPtr
                    >,
                #extra_constraints_ptr
            {
                let _can_it_downcast=can_it_downcast;
                unsafe{
                    Self{
                        obj:#trait_backend::with_vtable::<_,#fn_can_it_downcast_arg>(
                            ptr,
                            #make_vtable_ident::<#make_vtable_args>::VTABLE_INNER
                        ),
                        _marker:__sabi_re::UnsafeIgnoredType::DEFAULT,
                    }
                }
            }

            /// Constructs this trait object from its underlying implementation.
            ///
            /// This method is automatically generated,
            /// for more documentation you can look at
            /// [`abi_stable::docs::sabi_trait_inherent#from_sabi-method`]
            #submod_vis fn from_sabi(obj:#trait_backend<#uto_params_use>)->Self{
                Self{
                    obj,
                    _marker:__sabi_re::UnsafeIgnoredType::DEFAULT,
                }
            }

            #reborrow_methods
        }

        impl<#gen_params_header_rbox> #trait_to<#gen_params_use_to_rbox> {
            #[doc=#from_value_docs]
            #[doc=#shared_docs]
            #submod_vis fn from_value<_Self,Downcasting>(
                ptr:_Self,
                can_it_downcast:Downcasting,
            )->Self
            where
                _Self:
                    #trait_ident<#trait_params #( #assoc_tys_c= #assoc_tys_d, )* >
                    #plus_lt,
                #trait_interface<#trait_interface_use>:
                    __sabi_re::GetRObjectVTable<
                        Downcasting,_Self,__sabi_re::RBox<()>,__sabi_re::RBox<_Self>
                    >,
                #extra_constraints_value
            {
                Self::from_ptr::<
                    __sabi_re::RBox<_Self>,
                    Downcasting
                >(__sabi_re::RBox::new(ptr),can_it_downcast)
            }
        }

        impl<#gen_params_header_rref> #trait_to<#gen_params_use_to_rref>{
            #[doc=#from_const_docs]
            #[doc=#shared_docs]
            #submod_vis const fn from_const<_Self,Downcasting>(
                ptr:&'_sub _Self,
                can_it_downcast:Downcasting,
            )->Self
            where
                _Self:
                    #trait_ident<#trait_params #( #assoc_tys_e = #assoc_tys_f, )* >
                    #plus_lt,
                _Self: #one_lt
                #trait_interface<#trait_interface_use>:
                    __sabi_re::GetRObjectVTable<
                        Downcasting, _Self, __sabi_re::RRef<'_sub, ()>, &'_sub _Self
                    >,
                #extra_constraints_const
            {
                unsafe{
                    Self{
                        obj:#constructing_backend,
                        _marker:__sabi_re::UnsafeIgnoredType::DEFAULT,
                    }
                }
            }
        }

    )
    .to_tokens(mod_);
}

/// Returns a tokenizer for the reborrowing methods
fn reborrow_methods_tokenizer(
    TokenizerParams {
        totrait_def,
        submod_vis,
        trait_to,
        lt_tokens,
        ..
    }: TokenizerParams<'_>,
) -> impl ToTokens + '_ {
    ToTokenFnMut::new(move |ts| {
        let traits = totrait_def.trait_flags;
        // If the trait object doesn't have both Sync+Send as supertraits or neither,
        // it can't be reborrowed.
        if traits.sync != traits.send {
            return;
        }

        let gen_params_use_ref = totrait_def.generics_tokenizer(
            InWhat::ItemUse,
            WithAssocTys::Yes(WhichSelf::NoSelf),
            &lt_tokens.lt_rref,
        );

        let gen_params_use_mut = totrait_def.generics_tokenizer(
            InWhat::ItemUse,
            WithAssocTys::Yes(WhichSelf::NoSelf),
            &lt_tokens.lt_rmut,
        );

        quote!(
            /// Reborrows this trait object to a reference-based trait object.
            ///
            /// This method is automatically generated,
            /// for more documentation you can look at
            /// [`abi_stable::docs::sabi_trait_inherent#sabi_reborrow-method`]
            #submod_vis fn sabi_reborrow<'_sub>(&'_sub self)->#trait_to<#gen_params_use_ref> {
                let x = self.obj.reborrow();
                // This is transmuting the pointer type parameter of the vtable.
                let x = unsafe{ __sabi_re::transmute(x) };
                #trait_to::from_sabi(x)
            }

            /// Reborrows this trait object to a mutable-reference-based trait object.
            ///
            /// This method is automatically generated,
            /// for more documentation you can look at
            /// [`abi_stable::docs::sabi_trait_inherent#sabi_reborrow_mut-method`]
            #submod_vis fn sabi_reborrow_mut<'_sub>(&'_sub mut self)->#trait_to<#gen_params_use_mut>
            where
                _ErasedPtr: __sabi_re::AsMutPtr<PtrTarget=()>
            {
                let x = self.obj.reborrow_mut();
                // This is transmuting the pointer type parameter of the vtable.
                let x = unsafe{ __sabi_re::transmute(x) };
                #trait_to::from_sabi(x)
            }
        )
        .to_tokens(ts);
    })
}

/// Outputs the annotated trait (as modified by the proc-macro)
/// and an implementation of the trait for the generated trait object.
fn trait_and_impl(
    TokenizerParams {
        ctokens,
        submod_vis,
        trait_def,
        trait_to,
        lt_tokens,
        trait_ident,
        ..
    }: TokenizerParams,
    mod_: &mut TokenStream2,
) {
    let other_attrs = trait_def.other_attrs;
    let gen_params_trait =
        trait_def.generics_tokenizer(InWhat::ItemDecl, WithAssocTys::No, &ctokens.empty_ts);
    let where_preds = (&trait_def.where_preds).into_iter();
    let where_preds_b = where_preds.clone();
    let methods_tokenizer_def = trait_def.methods_tokenizer(WhichItem::Trait);
    let methods_tokenizer_impl = trait_def.methods_tokenizer(WhichItem::TraitImpl);
    let lifetime_bounds_a = trait_def.lifetime_bounds.iter();
    let lifetime_bounds_c = trait_def.lifetime_bounds.iter();
    let super_traits_a = trait_def.impld_traits.iter().map(|t| &t.bound);
    let super_traits_b = super_traits_a.clone();

    let assoc_tys_a = trait_def.assoc_tys.values().map(|x| &x.assoc_ty);

    let unsafety = trait_def.item.unsafety;

    let erased_ptr_bounds = trait_def.erased_ptr_preds();

    quote!(
        #[allow(clippy::needless_lifetimes, clippy::new_ret_no_self)]
        #( #other_attrs )*
        #submod_vis #unsafety trait #trait_ident<
            #gen_params_trait
        >: #( #super_traits_a + )* #( #lifetime_bounds_a + )*
        where
            #(#where_preds,)*
        {
            #( #assoc_tys_a )*

            #methods_tokenizer_def
        }
    )
    .to_tokens(mod_);

    let gen_params_use_trait =
        trait_def.generics_tokenizer(InWhat::ItemUse, WithAssocTys::No, &ctokens.empty_ts);

    if !trait_def.disable_trait_impl {
        let gen_params_header = trait_def.generics_tokenizer(
            InWhat::ImplHeader,
            WithAssocTys::Yes(WhichSelf::NoSelf),
            &lt_tokens.lt_erasedptr,
        );
        let gen_params_use_to = trait_def.generics_tokenizer(
            InWhat::ItemUse,
            WithAssocTys::Yes(WhichSelf::NoSelf),
            &lt_tokens.lt_erasedptr,
        );

        let assoc_ty_named_a = trait_def.assoc_tys.values().map(|x| &x.assoc_ty.ident);
        let assoc_ty_named_b = assoc_ty_named_a.clone();

        quote!(
            #[deny(unsafe_op_in_unsafe_fn)]
            #[allow(
                clippy::needless_lifetimes,
                clippy::new_ret_no_self,
            )]
            impl<#gen_params_header> #trait_ident<#gen_params_use_trait>
            for #trait_to<#gen_params_use_to>
            where
                Self:#( #super_traits_b + )* #(#lifetime_bounds_c+)*  ,
                #erased_ptr_bounds
                #(#where_preds_b,)*
            {
                #( type #assoc_ty_named_a=#assoc_ty_named_b; )*

                #methods_tokenizer_impl
            }
        )
        .to_tokens(mod_);
    }
}

/// An inherent implementation of the generated trait object,
/// which mirrors the trait definition.
fn methods_impls(param: TokenizerParams, mod_: &mut TokenStream2) -> Result<(), syn::Error> {
    let TokenizerParams {
        totrait_def,
        trait_to,
        ctokens,
        lt_tokens,
        ..
    } = param;

    let impl_where_preds = totrait_def.trait_impl_where_preds()?;

    let super_traits_a = totrait_def.impld_traits.iter().map(|t| &t.bound);

    let gen_params_header = totrait_def.generics_tokenizer(
        InWhat::ImplHeader,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_erasedptr,
    );
    let gen_params_use_to = totrait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &lt_tokens.lt_erasedptr,
    );

    let generics_use1 = totrait_def.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_unit_erasedptr,
    );

    let methods_tokenizer_def = totrait_def.methods_tokenizer(WhichItem::TraitObjectImpl);

    quote!(
        #[allow(clippy::needless_lifetimes, clippy::new_ret_no_self)]
        impl<#gen_params_header> #trait_to<#gen_params_use_to>
        where
            _ErasedPtr: __sabi_re::AsPtr<PtrTarget = ()>,
            Self:#( #super_traits_a + )*  ,
            #impl_where_preds
        {
            #[inline]
            fn sabi_vtable(
                &self
            )-> VTable_Ref<#generics_use1> {
                unsafe{
                    VTable_Ref(self.obj.sabi_et_vtable())
                }
            }

            #methods_tokenizer_def
        }
    )
    .to_tokens(mod_);

    Ok(())
}

/// Outputs the vtable struct.
fn declare_vtable(
    TokenizerParams {
        ctokens,
        vtable_trait_decl,
        submod_vis,
        trait_interface,
        ..
    }: TokenizerParams,
    mod_: &mut TokenStream2,
) {
    let generics_decl = vtable_trait_decl.generics_tokenizer(
        InWhat::ItemDecl,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_self_erasedptr,
    );

    let mut generics_decl_unbounded = generics_decl;
    generics_decl_unbounded.set_no_bounds();

    let mut generics_use0 = vtable_trait_decl.generics_tokenizer(
        InWhat::DummyStruct,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_self_erasedptr,
    );
    generics_use0.set_no_bounds();

    let derive_attrs = vtable_trait_decl.derive_attrs;

    let methods_tokenizer = vtable_trait_decl.methods_tokenizer(WhichItem::VtableDecl);

    let lifetime_bounds = if vtable_trait_decl.lifetime_bounds.is_empty() {
        None
    } else {
        let mut lifetime_bounds = quote!(_Self:);
        for lt in &vtable_trait_decl.lifetime_bounds {
            lifetime_bounds.append_all(quote!(#lt +));
        }
        lifetime_bounds.append(parse_str_as_ident("Sized"));
        Some(lifetime_bounds)
    }
    .into_iter();

    let trait_interface_use = vtable_trait_decl.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::NoSelf),
        &ctokens.ts_empty,
    );

    let robject_vtable = quote!(
        __sabi_re::RObjectVtable_Ref<
            _Self,
            _ErasedPtr,
            #trait_interface<#trait_interface_use>
        >
    );

    quote!(

        #[repr(C)]
        #[derive(abi_stable::StableAbi)]
        #[sabi(kind(Prefix(prefix_ref = VTable_Ref)))]
        #[sabi(missing_field(panic))]
        #( #[sabi(prefix_bound(#lifetime_bounds))] )*
        #[sabi(bound(#robject_vtable: ::abi_stable::StableAbi))]
        #(#derive_attrs)*
        #[doc(hidden)]
        #submod_vis struct VTable<#generics_decl>
        where
            _ErasedPtr:__GetPointerKind,
        {
            _sabi_tys: __sabi_re::NonOwningPhantom<(#generics_use0)>,

            _sabi_vtable:#robject_vtable,

            #methods_tokenizer
        }
    )
    .to_tokens(mod_);
}

/// Outputs the vtable impl block with both:
///
/// - A constant where the vtable is constructed.
///
/// - The methods that the vtable is constructed with.
///
fn vtable_impl(
    TokenizerParams {
        ctokens,
        vtable_trait_impl,
        trait_interface,
        trait_ident,
        make_vtable_ident,
        lt_tokens,
        ..
    }: TokenizerParams,
    mod_: &mut TokenStream2,
) {
    let struct_decl_generics = vtable_trait_impl.generics_tokenizer(
        InWhat::ItemDecl,
        WithAssocTys::No,
        &ctokens.ts_getvtable_params,
    );

    let dummy_struct_tys = vtable_trait_impl.generics_tokenizer(
        InWhat::DummyStruct,
        WithAssocTys::No,
        &ctokens.ts_getvtable_dummy_struct_fields,
    );

    let impl_header_generics = vtable_trait_impl.generics_tokenizer(
        InWhat::ImplHeader,
        WithAssocTys::No,
        &ctokens.ts_getvtable_params,
    );

    let makevtable_generics = vtable_trait_impl.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::No,
        &ctokens.ts_getvtable_params,
    );

    let trait_generics =
        vtable_trait_impl.generics_tokenizer(InWhat::ItemUse, WithAssocTys::No, &ctokens.empty_ts);

    let withmetadata_generics = vtable_trait_impl.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::Underscore),
        &ctokens.ts_self_erasedptr,
    );

    let trait_interface_use = vtable_trait_impl.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::Underscore),
        &ctokens.ts_empty,
    );

    let method_names_a = vtable_trait_impl.methods.iter().map(|m| m.name);
    let method_names_b = method_names_a.clone();

    let vtable_generics = vtable_trait_impl.generics_tokenizer(
        InWhat::ItemUse,
        WithAssocTys::Yes(WhichSelf::Underscore),
        &ctokens.ts_unit_erasedptr,
    );

    let methods_tokenizer = vtable_trait_impl.methods_tokenizer(WhichItem::VtableImpl);

    let one_lt = &lt_tokens.one_lt;

    let extra_constraints = match vtable_trait_impl.which_object {
        WhichObject::DynTrait => quote!(
            #trait_interface<#trait_interface_use>:
                ::abi_stable::erased_types::InterfaceType,

            __sabi_re::DynTraitVTable_Ref<
                #one_lt
                _ErasedPtr,
                #trait_interface<#trait_interface_use>,
            >:
                __sabi_re::MakeDynTraitVTable<
                    #one_lt
                    _Self,
                    _OrigPtr,
                    IA,
                >,
        ),
        WhichObject::RObject => quote!(),
    };

    quote!(
        struct #make_vtable_ident<#struct_decl_generics>(#dummy_struct_tys);

        #[deny(unsafe_op_in_unsafe_fn)]
        impl<#impl_header_generics> #make_vtable_ident<#makevtable_generics>
        where
            _Self: #trait_ident<#trait_generics>,
            _OrigPtr:
                __sabi_re::CanTransmuteElement<(), PtrTarget = _Self, TransmutedPtr = _ErasedPtr>,
            _ErasedPtr:__sabi_re::AsPtr<PtrTarget=()>,
            #trait_interface<#trait_interface_use>:
                __sabi_re::GetRObjectVTable<IA,_Self,_ErasedPtr,_OrigPtr>,
            #extra_constraints
        {
            const TMP0: __sabi_re::WithMetadata<
                VTable<#withmetadata_generics>
            >={
                __sabi_re::WithMetadata::new(
                    VTable{
                        _sabi_tys: __sabi_re::NonOwningPhantom::NEW,
                        _sabi_vtable:__sabi_re::GetRObjectVTable::ROBJECT_VTABLE,
                        #(
                            #method_names_a:Self::#method_names_b,
                        )*
                    }
                )
            };

            const VTABLE_INNER: __sabi_re::PrefixRef<VTable_Prefix<#vtable_generics> > =unsafe{
                __sabi_re::WithMetadata::raw_as_prefix(&Self::TMP0)
                    .cast() // erasing the `_Self` parameter
            };

            #methods_tokenizer
        }
    )
    .to_tokens(mod_);
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum SelfParam<'a> {
    ByRef {
        lifetime: Option<&'a syn::Lifetime>,
        is_mutable: bool,
    },
    ByVal,
}

/// Which item this is refering to.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum WhichItem {
    /// the method in the trait definition
    Trait,
    /// the method in the trait implemetation for the generated trait object.
    TraitImpl,
    /// the methods in the inherent implemetation of the generated trait object.
    TraitObjectImpl,
    /// the fields of the trait object vtable.
    VtableDecl,
    /// the methods used to construct the vtable.
    VtableImpl,
}

/// Which type used to implement the trait object.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum WhichObject {
    DynTrait,
    RObject,
}

impl Default for WhichObject {
    fn default() -> Self {
        WhichObject::RObject
    }
}

/// Which Self type to get the associated types from.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum WhichSelf {
    /// Self::AssocTy
    #[allow(dead_code)]
    Regular,
    /// _Self::AssocTy
    Underscore,
    /// <_OrigPtr as __Trait< <generic_params> >>::AssocTy
    #[allow(dead_code)]
    FullyQualified,
    /// AssocTy
    NoSelf,
}

/// Whether to include associated types when printing generic parameters.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum WithAssocTys {
    No,
    Yes(WhichSelf),
}
